#include "DataManager.h"
#include <windows.h>
#include <shlobj.h>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <algorithm>

DataManager::DataManager() 
    : m_currentClickCount(0)
    , m_unsavedClicks(0)
    , m_lastSaveTime(std::chrono::steady_clock::now())
{
    m_currentDate = GetCurrentDateString();
    m_dataFilePath = GetDataFilePath();
}

DataManager::~DataManager() {
    // Save data when program exits (save current state regardless of unsaved clicks)
    SaveData();
}

bool DataManager::Initialize() {
    // Create data directory
    size_t lastSlash = m_dataFilePath.find_last_of('\\');
    if (lastSlash != std::string::npos) {
        std::string dataDir = m_dataFilePath.substr(0, lastSlash);
        if (!CreateDirectoryIfNotExists(dataDir)) {
            return false;
        }
    }
    
    // Load historical data
    LoadDataFromFile();
    
    // Clean up expired data
    CleanupOldData();
    
    // Check if today's data exists
    std::string today = GetCurrentDateString();
    auto it = std::find_if(m_historyData.begin(), m_historyData.end(),
        [&today](const DailyData& data) { return data.date == today; });
    
    if (it != m_historyData.end()) {
        // Today's data already exists, load click count
        m_currentClickCount = it->clickCount;
    } else {
        // It's a new day, create a new record
        m_currentClickCount = 0;
        m_historyData.push_back(DailyData(today, 0));
    }
    
    return true;
}

void DataManager::OnKeyPressed() {
    m_currentClickCount++;
    
    // Check if maximum count reached, reset to 0 if exceeded
    if (m_currentClickCount > MAX_CLICK_COUNT) {
        m_currentClickCount = 0;
        m_unsavedClicks = 0; // Reset unsaved clicks as well since we're starting over
    } else {
        m_unsavedClicks++;
    }
    
    // Check if date has changed
    CheckDateChange();
    
    // Check if saving is needed
    if (ShouldSave()) {
        SaveData();
    }
}

void DataManager::SaveData() {
    // Update click count for current date
    std::string today = GetCurrentDateString();
    auto it = std::find_if(m_historyData.begin(), m_historyData.end(),
        [&today](const DailyData& data) { return data.date == today; });
    
    if (it != m_historyData.end()) {
        it->clickCount = m_currentClickCount;
    } else {
        m_historyData.push_back(DailyData(today, m_currentClickCount));
    }
    
    // Save to file
    if (SaveDataToFile()) {
        m_unsavedClicks = 0;
        m_lastSaveTime = std::chrono::steady_clock::now();
    }
}

void DataManager::ResetTodayCount() {
    // Reset today's count
    m_currentClickCount = 0;
    m_unsavedClicks = 0;
    
    // Update today's record in historical data
    std::string today = GetCurrentDateString();
    auto it = std::find_if(m_historyData.begin(), m_historyData.end(),
        [&today](const DailyData& data) { return data.date == today; });
    
    if (it != m_historyData.end()) {
        it->clickCount = 0;
    } else {
        m_historyData.push_back(DailyData(today, 0));
    }
    
    // Immediately save data after reset
    SaveDataToFile();
}

std::string DataManager::GetCurrentDateString() const {
    SYSTEMTIME st;
    GetLocalTime(&st);
    
    std::ostringstream oss;
    oss << std::setfill('0') << std::setw(4) << st.wYear << "-"
        << std::setfill('0') << std::setw(2) << st.wMonth << "-"
        << std::setfill('0') << std::setw(2) << st.wDay;
    
    return oss.str();
}

std::string DataManager::GetDataFilePath() const {
    char appDataPath[MAX_PATH];
    if (SHGetFolderPathA(NULL, CSIDL_APPDATA, NULL, 0, appDataPath) != S_OK) {
        return ""; // Return empty string to indicate error
    }
    
    std::string dataPath = std::string(appDataPath) + "\\NekoPa\\clicks.json";
    return dataPath;
}

bool DataManager::LoadDataFromFile() {
    if (!FileExists(m_dataFilePath)) {
        return true; // File not existing is normal (first run)
    }
    
    std::string jsonContent = ReadFileContent(m_dataFilePath);
    if (jsonContent.empty()) {
        return false;
    }
    
    return DeserializeFromJson(jsonContent);
}

bool DataManager::SaveDataToFile() {
    std::string jsonContent = SerializeToJson();
    return WriteFileContent(m_dataFilePath, jsonContent);
}

void DataManager::CleanupOldData() {
    // Get the date 30 days ago
    SYSTEMTIME st;
    GetLocalTime(&st);
    FILETIME ft, ft30DaysAgo;
    SystemTimeToFileTime(&st, &ft);
    
    // Calculate the time 30 days ago (30 days * 24 hours * 60 minutes * 60 seconds * 10,000,000 (100 nanosecond units))
    ULARGE_INTEGER uli;
    uli.LowPart = ft.dwLowDateTime;
    uli.HighPart = ft.dwHighDateTime;
    uli.QuadPart -= 30LL * 24 * 60 * 60 * 10000000;
    
    ft30DaysAgo.dwLowDateTime = uli.LowPart;
    ft30DaysAgo.dwHighDateTime = uli.HighPart;
    
    SystemTimeToFileTime(&st, &ft30DaysAgo);
    SYSTEMTIME st30DaysAgo;
    FileTimeToSystemTime(&ft30DaysAgo, &st30DaysAgo);
    
    std::ostringstream oss;
    oss << std::setfill('0') << std::setw(4) << st30DaysAgo.wYear << "-"
        << std::setfill('0') << std::setw(2) << st30DaysAgo.wMonth << "-"
        << std::setfill('0') << std::setw(2) << st30DaysAgo.wDay;
    
    std::string cutoffDate = oss.str();
    
    // Remove data older than 30 days
    m_historyData.erase(
        std::remove_if(m_historyData.begin(), m_historyData.end(),
            [&cutoffDate](const DailyData& data) {
                return data.date < cutoffDate;
            }),
        m_historyData.end()
    );
}

void DataManager::CheckDateChange() {
    std::string newDate = GetCurrentDateString();
    if (newDate != m_currentDate) {
        // Date changed, immediately save current data
        SaveData();
        
        // Reset for a new day
        m_currentDate = newDate;
        m_currentClickCount = 0;
        m_unsavedClicks = 0;
        
        // Add record for new date
        m_historyData.push_back(DailyData(newDate, 0));
    }
}

bool DataManager::ShouldSave() const {
    // Check click count threshold
    if (m_unsavedClicks >= SAVE_INTERVAL_CLICKS) {
        return true;
    }
    
    // Check time interval
    auto now = std::chrono::steady_clock::now();
    auto timeSinceLastSave = std::chrono::duration_cast<std::chrono::seconds>(
        now - m_lastSaveTime).count();
    
    return timeSinceLastSave >= SAVE_INTERVAL_SECONDS;
}

std::string DataManager::SerializeToJson() const {
    std::ostringstream oss;
    oss << "{\n  \"data\": [\n";
    
    for (size_t i = 0; i < m_historyData.size(); ++i) {
        const auto& data = m_historyData[i];
        oss << "    {\n";
        oss << "      \"date\": \"" << data.date << "\",\n";
        oss << "      \"click_count\": " << data.clickCount << "\n";
        oss << "    }";
        
        if (i < m_historyData.size() - 1) {
            oss << ",";
        }
        oss << "\n";
    }
    
    oss << "  ]\n}";
    return oss.str();
}

bool DataManager::DeserializeFromJson(const std::string& jsonContent) {
    m_historyData.clear();
    
    // Simple JSON parsing (for our specific format)
    size_t dataPos = jsonContent.find("\"data\"");
    if (dataPos == std::string::npos) return false;
    
    size_t arrayStart = jsonContent.find("[", dataPos);
    if (arrayStart == std::string::npos) return false;
    
    size_t arrayEnd = jsonContent.find("]", arrayStart);
    if (arrayEnd == std::string::npos) return false;
    
    std::string arrayContent = jsonContent.substr(arrayStart + 1, arrayEnd - arrayStart - 1);
    
    // Parse each object
    size_t pos = 0;
    while (pos < arrayContent.length()) {
        size_t objStart = arrayContent.find("{", pos);
        if (objStart == std::string::npos) break;
        
        size_t objEnd = arrayContent.find("}", objStart);
        if (objEnd == std::string::npos) break;
        
        std::string objContent = arrayContent.substr(objStart + 1, objEnd - objStart - 1);
        
        // Parse date
        size_t datePos = objContent.find("\"date\"");
        if (datePos == std::string::npos) break;
        
        size_t dateValueStart = objContent.find("\"", datePos + 6);
        if (dateValueStart == std::string::npos) break;
        dateValueStart++;
        
        size_t dateValueEnd = objContent.find("\"", dateValueStart);
        if (dateValueEnd == std::string::npos) break;
        
        std::string date = objContent.substr(dateValueStart, dateValueEnd - dateValueStart);
        
        // Parse click_count
        size_t countPos = objContent.find("\"click_count\"");
        if (countPos == std::string::npos) break;
        
        size_t countValueStart = objContent.find(":", countPos);
        if (countValueStart == std::string::npos) break;
        countValueStart++;
        
        // Skip whitespace characters
        while (countValueStart < objContent.length() && 
               (objContent[countValueStart] == ' ' || objContent[countValueStart] == '\t')) {
            countValueStart++;
        }
        
        size_t countValueEnd = countValueStart;
        while (countValueEnd < objContent.length() && 
               std::isdigit(objContent[countValueEnd])) {
            countValueEnd++;
        }
        
        if (countValueEnd == countValueStart) break;
        
        std::string countStr = objContent.substr(countValueStart, countValueEnd - countValueStart);
        int clickCount = std::stoi(countStr);
        
        m_historyData.push_back(DailyData(date, clickCount));
        
        pos = objEnd + 1;
    }
    
    return true;
}

bool DataManager::CreateDirectoryIfNotExists(const std::string& path) const {
    DWORD dwAttrib = GetFileAttributesA(path.c_str());
    if (dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY)) {
        return true; // Directory already exists
    }
    
    return CreateDirectoryA(path.c_str(), NULL) != 0;
}

bool DataManager::FileExists(const std::string& path) const {
    DWORD dwAttrib = GetFileAttributesA(path.c_str());
    return (dwAttrib != INVALID_FILE_ATTRIBUTES && !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
}

std::string DataManager::ReadFileContent(const std::string& path) const {
    std::ifstream file(path, std::ios::binary);
    if (!file.is_open()) {
        return "";
    }
    
    std::ostringstream oss;
    oss << file.rdbuf();
    return oss.str();
}

bool DataManager::WriteFileContent(const std::string& path, const std::string& content) const {
    std::ofstream file(path, std::ios::binary);
    if (!file.is_open()) {
        return false;
    }
    
    file << content;
    return file.good();
}
